package main

import (
	"strings"
	"sync"
	"os"
	"strconv"
	"net"
	"fmt"
	"flag"
)

type server_info struct {
	ip string
	port int
}

var sinfo server_info
var sig sync.WaitGroup

func cmdline_parse () {
	flag.StringVar (&sinfo.ip, "ip", "0.0.0.0", "Server listen IP.")
	flag.IntVar (&sinfo.port, "port", 0, "Server listen PORT.")
	flag.Parse ()

	func () {
		fmt.Println ("Server listen IP : ", sinfo.ip)
		fmt.Println ("Server listen PORT : ", sinfo.port)
	} ()
}

func system_init () {
	cmdline_parse ()
}

func http_handler (con *net.TCPConn) {
	defer func (con *net.TCPConn) {
		err := con.Close ()
		if (err != nil) {
			fmt.Println ("Close connection error.")
			fmt.Println (err)
		}
	} (con)

	var data [10240]byte
	var header string
	var off int = -1
	var err error
	var len int
	for off == -1 {
		len, err = con.Read (data[:])
		if (len < 0 || err != nil) {
			fmt.Println ("Read error")
			fmt.Println (err)
			return
		}
		header += string (data[:len])
		off = parse_try_to_get_header (header)
	}

	parser := parse_header (strings.TrimLeft (header, " "))
	if (parser.fail == unimplement) {
		response_unimplement (con)
		return
	} else if (parser.fail == bad_request) {
		response_bad_request (con)
		return
	}

	_, err = os.Stat (parser.url)
	if (err != nil) {
		response_not_found (con)
		return
	}

	if (parser.cgi == false) {
		response_server_file (con, parser.url)
		return
	}

	if (parser.cgi == true) {
		var content string
		/* Double /r/n.  */
		if (parser.method == post) {
			clen, _ := strconv.Atoi (parser.content_len)
			content += string (data[off + 4:len])
			clen = clen - (len - off - 4)
			for clen > 0 {
				var buf [10240]byte
				res, err := con.Read (buf[:])
				if (res == 0 || err != nil) {
					break
				}
				clen -= res
				content += string (buf[:res])
			}
		}
		cgi_execute (con, parser, content)
		return
	}

	response_unimplement (con)
}

func do_accept (l *net.TCPListener) {
	defer sig.Done ()

	var con *net.TCPConn
	var err error
	for {
		con, err = l.AcceptTCP ()
		if (err != nil) {
			fmt.Println (err)
			break
		}

		fmt.Println ("Accept connection from ", con.RemoteAddr ())
		go http_handler (con)
	}
}

func server_startup () {
	defer fmt.Println ("Exit server.")
	serv := sinfo.ip + ":" + strconv.Itoa (sinfo.port)
	tcp_addr, err := net.ResolveTCPAddr ("tcp4", serv)
	check_error (err)

	listener, err := net.ListenTCP ("tcp", tcp_addr)
	check_error (err)
	defer func (l *net.TCPListener) {
		err = l.Close ()
		check_error (err)
	} (listener)

	fmt.Println ("Start listen.")
	fmt.Println (listener.Addr ())

	sig.Add (1)
	go do_accept (listener)

	sig.Wait ()
}

func main () {
	system_init ()

	server_startup ()
}

func check_error (err error) {
	if (err != nil) {
		fmt.Println (err)
		os.Exit (1)
	}
}
